#region License

/* Copyright (c) 2008, Dusty Candland
 * 
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
 * * Neither the name of Dusty Candland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#endregion

using System.Collections.Generic;
using System.Data.SQLite;

namespace NS3Library.SQLite
{
    internal class BucketRepository : BaseRepository
    {
        private const string SELECT_OBJECT_KEYS =
            "select id, bucket_id, key, last_modified, etag, size, owner_id, storage_class from objects where bucket_id = @bucket_id";

        private const string DELETE_FOR_NAME =
            "delete from objects where bucket_id in (select id from buckets where name = @name); delete from buckets where name = @name;";

        private const string CREATE_TABLE =
            @"CREATE TABLE IF NOT EXISTS buckets (
				id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, creation_date TEXT, location INTEGER, acl TEXT)";

        private const string INSERT = "insert into buckets (name, location, acl) values (@name, @location, @acl)";
        private const string SELECT_ID = "select id from buckets where name = @name;";
        private const string UPDATE = "update buckets set location = @location, acl = @acl where id = @id";
        private const string SELECT = "select id, name, creation_date, location, acl from buckets where id = @id;";
        private const string SELECT_ALL_IDS = "select id from buckets;";

        public BucketRepository(string connectionString) : base(connectionString)
        {
        }

        public void EnsureBucketsTable()
        {
            ExecuteNonQuery(CREATE_TABLE);
        }

        public void Insert(IS3Bucket s3Bucket)
        {
            ExecuteNonQuery(INSERT,
                            new SQLiteParameter("@name", s3Bucket.Name),
                            new SQLiteParameter("@location", s3Bucket.LocationConstraint),
                            new SQLiteParameter("@acl", (s3Bucket.AclHeader ?? S3Acl.Private).ToString())
                );
        }

        public int GetBucketId(string name)
        {
            var buckets = new List<int>(
                ExecuteReader(SELECT_ID,
                              SQLiteS3Parser.IdParserDelegate,
                              new SQLiteParameter("@name", name)));

            if (buckets.Count > 0)
                return buckets[0];

            return 0;
        }

        public void Update(int id, IS3Bucket s3Bucket)
        {
            ExecuteNonQuery(UPDATE,
                            new SQLiteParameter("@id", id),
                            new SQLiteParameter("@location", s3Bucket.LocationConstraint),
                            new SQLiteParameter("@acl", (s3Bucket.AclHeader ?? S3Acl.Private).ToString())
                );
        }

        public SQLiteS3Bucket Find(int id)
        {
            var buckets = new List<SQLiteS3Bucket>(
                ExecuteReader(SELECT,
                              SQLiteS3Parser.BucketParserDelegate,
                              new SQLiteParameter("@id", id)));

            if (buckets.Count <= 0)
                return null;

            SQLiteS3Bucket bucket = buckets[0];

            var list = new List<SQLiteS3ObjectKey>(ExecuteReader(SELECT_OBJECT_KEYS,
                                                                 SQLiteS3Parser.ParseObjectKeyDelegate,
                                                                 new SQLiteParameter("@bucket_id", bucket.Id)));

            return new SQLiteS3Bucket(bucket.Id, bucket.Name, bucket.Prefix, bucket.MaxKeys,
                                      bucket.Marker, bucket.Delimiter, bucket.IsTruncated, list.ToArray());
        }

        public IS3Bucket Find(string bucketName)
        {
            return Find(GetBucketId(bucketName));
        }

        public IEnumerable<IS3Bucket> FindAll()
        {
            IEnumerable<int> ids = ExecuteReader(SELECT_ALL_IDS, SQLiteS3Parser.IdParserDelegate);

            var withKeys = new List<IS3Bucket>();
            foreach (int id in ids)
                withKeys.Add(Find(id));

            return withKeys;
        }

        public bool Exists(string bucketName)
        {
            return GetBucketId(bucketName) != 0;
        }

        public void Delete(string bucketName)
        {
            ExecuteNonQuery(DELETE_FOR_NAME, new SQLiteParameter("@name", bucketName));
        }
    }
}